package org.xtext.runtime.plan;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.xtext.runtime.interpreter.Agent;
import org.xtext.runtime.term.Variable;
import org.xtext.runtime.util.Bindings;


public class BlockStep implements IPlanStep {
	private IPlanStep[] plan;
	int planState;
	int step;
	
	public BlockStep(IPlanStep[] plan) {
		this.plan = plan;
		planState = 0;
		step = 0;
	}

	public String toString() {
		return toString("");
	}

	public String toString(String tabs) {
		int s = 0;
		if (step > 0) s = step-1;
		if (s == plan.length) return "{}";
		else return "{" + plan[s].toString("") + "}";
	}

	public String toFullString(String tabs) {
		String output = "{\n";
		for (IPlanStep s : plan) {
			output += s.toFullString(tabs + IPlanStep.TAB) + "\n";
		}
		return output + tabs + "}";
	}
	
	public IPlanStep[] plan() {
		return plan;
	}

	@Override
	public void handle(Agent agent, PlanStack planStack) {
		if (plan.length != 0) {
			switch (planState) {
			case 0:
				step = 0;

				
				planStack.back();
				planStack.addPlanStep(plan[step++]);

				// start the first step...
				planState = 1;
				planStack.getNextActivity().handle(agent, planStack);
//				System.out.println("plan: " + this.toFullString(""));
				break;
			case 1:
				// check in case it is a single statement block
				if (step < plan.length) {
					planStack.back();
					planStack.addPlanStep(plan[step++]);
					// start the next step...
					planStack.getNextActivity().handle(agent, planStack);
//					System.out.println("plan: " + this.toFullString(""));
				} else {
					planState = 0;
					step = 0;
					IPlanStep next = planStack.getNextActivity();
					if (next != null) next.handle(agent, planStack);
				}
			}
		}
	}

	@Override
	public Set<Variable> variables() {
		Set<Variable> variables = new HashSet<Variable>();
		for (IPlanStep pstep : this.plan) {
			for (Variable var : pstep.variables()) {
				if (!variables.contains(var)) variables.add(var);
//				else System.out.println("Duplicate: " + var);
			}
		}
		return variables;
	}

	@Override
	public BlockStep copy(Map<String, Variable> vars) {
		IPlanStep[] steps = new IPlanStep[plan.length];
		for (int i=0; i<plan.length; i++) {
			steps[i] = plan[i].copy(vars);
		}
		return new BlockStep(steps);
	}

	@Override
	public BlockStep apply(Bindings bindings) {
//		System.out.println("applying: " + bindings + " to: " + this);
		for (int i=0; i<plan.length; i++) {
			plan[i] = plan[i].apply(bindings);
//			System.out.println("AFTER STEP " + i + ": " + variables());
		}
//		System.out.println("finished applying: " + bindings + " to: " + this);
		return this;
	}

	@Override
	public void reset() {
		planState = 0;
		for (int i=0; i<plan.length; i++) {
			plan[i].reset();
		}
	}
}
